package entities;

import entities.extra.UserTripParticipation;
import entities.location.Address;
import entities.reports.ReportedTrip;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Trabalho PD - 2014/2015
 *
 * @author a21210380 a21210392
 */
@Entity
@Table(name = "trip")
@NamedQueries({
    @NamedQuery(name = "Trip.findAll", query = "SELECT t FROM Trip t"),
    @NamedQuery(name = "Trip.findByIdTrip", query = "SELECT t FROM Trip t WHERE t.idTrip = :idTrip"),
    @NamedQuery(name = "Trip.findByDescription", query = "SELECT t FROM Trip t WHERE t.description = :description"),
    @NamedQuery(name = "Trip.findByDepartueEstimatedDate", query = "SELECT t FROM Trip t WHERE t.departueEstimatedDate = :departueEstimatedDate"),
    @NamedQuery(name = "Trip.findByArrivalEstimatedDate", query = "SELECT t FROM Trip t WHERE t.arrivalEstimatedDate = :arrivalEstimatedDate"),
    @NamedQuery(name = "Trip.findUnfinishedTrips", query = "SELECT t FROM Trip t WHERE t.ended = false"),
    @NamedQuery(name = "Trip.findByVehicleId", query = "SELECT t FROM Trip t WHERE t.idVehicle.idVehicle = ?1"),
    @NamedQuery(name = "Trip.findRecent", query = "SELECT t FROM Trip t WHERE t.ended = false ORDER BY t.departueEstimatedDate ASC"),
    @NamedQuery(name = "Trip.searchByEstimatedDepartureDate", query = "SELECT t FROM Trip t WHERE t.departueEstimatedDate >= ?1"),
    @NamedQuery(name = "Trip.searchByStartingPlace", query = "SELECT t FROM Trip t WHERE t.idDeparture.idAddress = ?1"),
    @NamedQuery(name = "Trip.searchByStartingPlaceAndDate", query = "SELECT t FROM Trip t WHERE t.idDeparture.idAddress = ?1 AND t.departueEstimatedDate >= ?2"),
    @NamedQuery(name = "Trip.searchByEndingPlace", query = "SELECT t FROM Trip t WHERE t.idDestination.idAddress = ?1"),
    @NamedQuery(name = "Trip.searchByStartingAndEndingPlace", query = "SELECT t FROM Trip t WHERE t.idDestination.idAddress = ?1 AND t.idDeparture.idAddress = ?2"),
    @NamedQuery(name = "Trip.searchByStartingAndEndingPlaceAndDate", query = "SELECT t FROM Trip t WHERE t.idDestination.idAddress = ?1 AND t.idDeparture.idAddress = ?2 AND t.departueEstimatedDate >= ?3"),
    @NamedQuery(name = "Trip.findAllTripsCreatedBy", query = "SELECT t FROM Trip t JOIN t.idVehicle v WHERE v.idUser.idUser = ?1")})
public class Trip implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idTrip")
    private Integer idTrip;

    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 700)
    @Column(name = "description")
    private String description;

    @Basic(optional = false)
    @NotNull
    @Column(name = "departueEstimatedDate")
    @Temporal(TemporalType.TIMESTAMP)
    private Date departueEstimatedDate;

    @Basic(optional = false)
    @NotNull
    @Column(name = "ended")
    private boolean ended;

    @Basic(optional = false)
    @NotNull
    @Column(name = "arrivalEstimatedDate")
    @Temporal(TemporalType.TIMESTAMP)
    private Date arrivalEstimatedDate;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idTrip")
    private Collection<ReportedTrip> reports;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "trip")
    private Collection<UserTripParticipation> participations;

    @JoinColumn(name = "idVehicle", referencedColumnName = "idVehicle")
    @ManyToOne(optional = false)
    private Vehicle idVehicle;

    @JoinColumn(name = "idDeparture", referencedColumnName = "idAddress")
    @ManyToOne(optional = false)
    private Address idDeparture;

    @JoinColumn(name = "idDestination", referencedColumnName = "idAddress")
    @ManyToOne(optional = false)
    private Address idDestination;

    @JoinTable(name = "trip_evaluation", joinColumns = {
        @JoinColumn(name = "idTrip", referencedColumnName = "idTrip")}, inverseJoinColumns = {
        @JoinColumn(name = "idUser", referencedColumnName = "idUser")})
    @ManyToMany
    private Collection<User> missingEvaluations;

    public Trip() {
    }

    public Trip(Vehicle vehicle, Address departure, Address destination, String description, Date departueEstimatedDate, Date arrivalEstimatedDate) {
        this.idVehicle = vehicle;
        this.idDeparture = departure;
        this.idDestination = destination;
        this.description = description;
        this.departueEstimatedDate = departueEstimatedDate;
        this.arrivalEstimatedDate = arrivalEstimatedDate;
        this.ended = false;

        this.reports = new ArrayList<>();
        this.participations = new ArrayList<>();
        this.missingEvaluations = new ArrayList<>();
    }
    
    public void addToEvaluation(User t) {
        missingEvaluations.add(t);
    }

    public Integer getId() {
        return idTrip;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Date getDepartueEstimatedDate() {
        return departueEstimatedDate;
    }

    public void setDepartueEstimatedDate(Date departueEstimatedDate) {
        this.departueEstimatedDate = departueEstimatedDate;
    }

    public Date getArrivalEstimatedDate() {
        return arrivalEstimatedDate;
    }

    public void setArrivalEstimatedDate(Date arrivalEstimatedDate) {
        this.arrivalEstimatedDate = arrivalEstimatedDate;
    }

    public Collection<ReportedTrip> getReports() {
        return reports;
    }

    public void reportTrip(ReportedTrip report) {
        this.reports.add(report);
    }

    public Collection<UserTripParticipation> getParticipations() {
        return participations;
    }

    public boolean userInTrip(User user) {
        for (UserTripParticipation p : participations) {
            if (Objects.equals(p.getTrip().getId(), idTrip) && Objects.equals(p.getUser().getId(), user.getId())) {
                return true;
            }
        }
        return false;
    }
    
    public boolean userAcceptedInTrip(User user) {
        for (UserTripParticipation p : participations) {
            if (Objects.equals(p.getTrip().getId(), idTrip) && Objects.equals(p.getUser().getId(), user.getId())) {
                return p.getAccepted();
            }
        }
        return false;
    }
    
    public UserTripParticipation getParticipation(User user) {
        for (UserTripParticipation p : participations) {
            if (Objects.equals(p.getTrip().getId(), idTrip) && Objects.equals(p.getUser().getId(), user.getId())) {
                return p;
            }
        }
        return null;
    }

    public void addParticipation(UserTripParticipation userTripParticipationCollection) {
        this.participations.add(userTripParticipationCollection);
    }

    public Vehicle getVehicle() {
        return idVehicle;
    }

    public Address getDeparture() {
        return idDeparture;
    }

    public Address getDestination() {
        return idDestination;
    }

    public void endTrip() {
        ended = true;
    }

    public boolean isEnded() {
        return ended;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (idTrip != null ? idTrip.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        if (object == null) {
            return false;
        }
        if (!(object instanceof Trip)) {
            return false;
        }
        Trip other = (Trip) object;
        return !((this.idTrip == null && other.idTrip != null) || (this.idTrip != null && !this.idTrip.equals(other.idTrip)));
    }

    @Override
    public String toString() {
        return "entities.Trip[ idTrip=" + idTrip + " ]";
    }

    public Collection<User> getMissingEvaluations() {
        return missingEvaluations;
    }
    
    public boolean didUserEvaluatedThisTrip(User user) {
        for(User s : missingEvaluations) {
            if(Objects.equals(s.getId(), user.getId())) {
                return false;
            }
        }
        return true;
    }
    
    public void userEvaluatedTrip(User user) {
        missingEvaluations.remove(user);
    }

}
